package cn.jtfadmin.base.doc.swagger;

import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.*;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.common.Compatibility;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.RequestParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spi.service.contexts.RequestMappingContext;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER+1)
public class DocOperationImplicitParameterReader implements OperationBuilderPlugin {

    private final JavaProjectBuilder javaProjectBuilder;
    @Autowired
    public DocOperationImplicitParameterReader( JavaProjectBuilder javaProjectBuilder) {
        this.javaProjectBuilder = javaProjectBuilder;
    }
    @Override
    public void apply(OperationContext context) {
        List<Compatibility<Parameter, RequestParameter>> parameters
                = readParameters(context);
        context.operationBuilder().parameters(parameters.stream()
                .map(Compatibility::getLegacy)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList()));
        context.operationBuilder().requestParameters(parameters.stream()
                .map(Compatibility::getModern)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList()));
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    private List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>>
    readParameters(OperationContext context) {


        try{
            Field f1 = ReflectionUtils.findField(context.getClass(), "requestContext");
            ReflectionUtils.makeAccessible(f1);
            RequestMappingContext requestMappingContext = (RequestMappingContext) f1.get(context);
            Field f2 = ReflectionUtils.findField(RequestMappingContext.class, "handler");
            ReflectionUtils.makeAccessible(f2);
            RequestHandler requestHandler = (RequestHandler) f2.get(requestMappingContext);
            HandlerMethod handlerMethod = requestHandler.getHandlerMethod();
            if(handlerMethod!=null){
                Method method = handlerMethod.getMethod();
                Class<?> c = method.getDeclaringClass();
                String name = method.getName();
                Class<?>[] genericParameterTypes = method.getParameterTypes();
                JavaClass javaClass = javaProjectBuilder.getClassByName(c.getName());
                if(javaClass!=null){
                    List<JavaMethod> methods =
                            javaClass.getMethods(true);
                    for (JavaMethod javaMethod : methods) {
                        boolean match = true;
                        if(!javaMethod.getName().equals(name)){
                            match = false;
                        }
                        List<JavaType> parameterTypes = javaMethod.getParameterTypes();
                        if(match){
                            if(parameterTypes.size() != genericParameterTypes.length){
                                match = false;
                            }
                        }
                        for (int i = 0; i < parameterTypes.size(); i++) {
                            if(! parameterTypes.get(i).getFullyQualifiedName().equals(genericParameterTypes[i].getName())){
                                match = false;
                                break;
                            }

                        }
                        if(match){
                            List<Compatibility<springfox.documentation.service.Parameter, RequestParameter>> result = new ArrayList<>(parameterTypes.size());
                            for (JavaParameter parameter : javaMethod.getParameters()) {
                                String comment = null;
                                List<DocletTag> param = javaMethod.getTagsByName("param");
                                for (DocletTag tag : ObjectUtils.defaultIfNull(param, Collections.<DocletTag>emptyList())) {
                                    List<String> parameters = tag.getParameters();
                                    if(parameters.size()>1 && parameter.getName().equals(parameters.get(0))){
                                        comment = StringUtils.collectionToCommaDelimitedString(parameters.subList(1, parameters.size()));
                                    }
                                }
                                result.add(
                                        new Compatibility<>(
                                                new springfox.documentation.builders.ParameterBuilder()
                                                        .name(parameter.getName())
                                                        .description(
                                                                comment
                                                        )
                                                        .order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER-1)
                                                        .build(),
                                                new RequestParameterBuilder()
                                                        .name(parameter.getName())
                                                        .description(comment)
                                                        .precedence(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER-1)
                                                        .build()
                                        )
                                );
                            }

                            return result;
                        }

                    }
                }
            }
        }catch (Exception e){

        }
        return Collections.emptyList();
    }

}
